package com.liao.cloudstorage.controller;


import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.liao.cloudstorage.conf.MinioClientBean;
import com.liao.cloudstorage.entity.Bucket;
import com.liao.cloudstorage.entity.FileObj;
import com.liao.cloudstorage.service.IBucketService;
import com.liao.cloudstorage.service.IFileObjService;
import io.minio.MinioClient;
import io.minio.errors.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.xmlpull.v1.XmlPullParserException;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.time.LocalDateTime;
import java.util.List;

import static com.liao.cloudstorage.conf.CodeInfo.*;

/**
 * <p>
 * 前端控制器
 * </p>
 *
 * @author liao
 * @since 2019-04-29
 */
@Controller
@RequestMapping("/")
public class FileObjController {
    private static Logger logger = LoggerFactory.getLogger(FileObjController.class);
    private MinioClient minioClient;
    private final IFileObjService fileObjService;

    private final IBucketService bucketService;

    @Autowired
    public FileObjController(MinioClientBean minioClientBean, IFileObjService fileObjService, IBucketService bucketService) {
        minioClient = minioClientBean.getMinioClient();
        this.fileObjService = fileObjService;
        this.bucketService = bucketService;
    }

    @GetMapping("/uploadFileList")
    public String showFileList(Model model) {
        List<FileObj> allFile = fileObjService.getAllFile();
        logger.debug("文件列表--->{}", allFile);
        model.addAttribute("objectList", allFile);
        return "uploadFileList";
    }

    /**
     * 文件上传
     *
     * @param bucketName 桶名称
     * @param file       文件
     * @return json
     */
    @ResponseBody
    @Transactional(rollbackFor = Exception.class)
    @PostMapping("/fileUpload/")
    public Object upload(String bucketName, MultipartFile file) {
        logger.debug("bucketName-->{}", bucketName);
        ModelMap modelMap = new ModelMap();
        try {
            InputStream inputStream = file.getInputStream();
            String objectName = file.getOriginalFilename();
            logger.debug("filename--->{}", objectName);
            if (objectIsExist(bucketName, objectName)) {
                modelMap.addAttribute("code", OBJECT_EXIST);
                modelMap.addAttribute("info", "该名称的文件已经存在");
            } else {
                minioClient.putObject(bucketName, objectName, inputStream, "application/octet-stream");
                FileObj fileObj = new FileObj();
                fileObj.setBucketName(bucketName);
                fileObj.setObjectName(objectName);
                double fileSize = file.getSize() / 1024.0 / 1024.0;
                BigDecimal bg = new BigDecimal(fileSize);
                fileSize = bg.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
                logger.debug("文件大小-->{}", fileSize);
                fileObj.setObjectSize(fileSize);
                fileObj.setUploadDate(LocalDateTime.now());
                fileObjService.save(fileObj);
                bucketService.autoAddFile(bucketName, fileSize);
                logger.debug("fileObjInfo--->{}", fileObj);
                modelMap.addAttribute("info", "文件上传成功");
                modelMap.addAttribute("code", SUCCESS);
            }
        } catch (IOException | XmlPullParserException | InvalidBucketNameException
                | NoSuchAlgorithmException | InvalidArgumentException | InsufficientDataException
                | InvalidKeyException | ErrorResponseException | NoResponseException
                | InternalException e) {
            logger.error("错误信息--->{}\n----->{}", FileObjController.class, e.getMessage());
            modelMap.addAttribute("err", "err");
            modelMap.addAttribute("code", ERROR);
        }
        return JSON.toJSON(modelMap);
    }


    private boolean objectIsExist(String bucketName, String objectName) {
        try {
            minioClient.statObject(bucketName, objectName);
            logger.debug("文件已经存在");
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    @GetMapping("/fileUpload")
    public String text(ModelMap modelMap) {
        List<Bucket> bucketList = bucketService.getAllBucket();
        logger.debug("存储桶-->{}", bucketList);
        modelMap.addAttribute("bucketList", bucketList);
        return "fileUpload";
    }

    @ResponseBody
    @Transactional(rollbackFor = Exception.class)
    @PostMapping("/file/delete/")
    public Object deleteFile(Integer id) {
        logger.debug("id--->{}", id);
        ModelMap modelMap = new ModelMap();
        FileObj fileObj = fileObjService.getById(id);
        try {
            // 删除数据库中的文件信息
            boolean b = fileObjService.autoRemoveFile(id);
            // 删除文件
            minioClient.removeObject(fileObj.getBucketName(), fileObj.getObjectName());
            if (b) {
                modelMap.addAttribute("code", SUCCESS);
                modelMap.addAttribute("info", "删除成功");
            } else {
                modelMap.addAttribute("info", "删除失败");
            }
        } catch (Exception e) {
            modelMap.addAttribute("info", "删除失败");
            e.printStackTrace();
        }
        return JSON.toJSON(modelMap);
    }

    /**
     * 文件下载
     *
     * @param id       id
     * @param response res
     */
    @GetMapping("/file/download/{id}")
    public void downloadFile(HttpServletResponse response, @PathVariable Integer id) {
        FileObj fileObj = fileObjService.getById(id);
        downloadUtils(response, fileObj, true);
    }


    @GetMapping("/file/download/file_name/{name}")
    public void downloadFile(HttpServletResponse response, @PathVariable String name) {
        FileObj fileObj = fileObjService.getFileObjByFileName(name);
        downloadUtils(response, fileObj, true);
    }

    @GetMapping("/file/download/{bucketName}/{fileName}")
    public void downloadFile(HttpServletResponse response, @PathVariable String bucketName, @PathVariable String fileName) {
        FileObj objByBucketAndFileName = fileObjService.getFileObjByBucketAndFileName(bucketName, fileName);
        logger.error("obj---->{}",objByBucketAndFileName);
        downloadUtils(response, objByBucketAndFileName, true);
    }

    @GetMapping("/file/preview/{id}")
    public void preViewFile(HttpServletResponse response, @PathVariable Integer id) {
        FileObj fileObj = fileObjService.getById(id);
        downloadUtils(response, fileObj, false);
    }


    private void downloadUtils(HttpServletResponse response, FileObj fileObj, boolean need) {
        if (fileObj != null) {
            if (need) {
                response.setContentType("application/force-download");
                response.addHeader("Content-Disposition", "attachment;fileName=" + fileObj.getObjectName());
            }
            byte[] bytes = new byte[8 * 1024];
            InputStream inputStream = null;
            OutputStream outputStream = null;
            try {
                int i = -1;
                inputStream = minioClient.getObject(fileObj.getBucketName(), fileObj.getObjectName());
                outputStream = response.getOutputStream();
                while (true) {
                    i = inputStream.read(bytes);
                    if (i < 0) {
                        break;
                    }
                    outputStream.write(bytes, 0, i);
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (inputStream != null) {
                    try {
                        inputStream.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if (outputStream != null) {
                    try {
                        outputStream.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }

            }
        }
    }

}
